#include <errno.h>
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdexcept>
#include <sstream>
#include <iostream>
#include "gpio_armeabi-v7a.h"

namespace gpio_syslib
{
    // const std::string pin::mapper("/sys/class/gpio/export");
    // const std::string pin::unmapper("/sys/class/gpio/unexport");
    pin::pin(const int& num)
        // fd_map(-1),
        // fd_unmap(-1),
        // num(-1)
    {
        std::stringstream ss;
        this->num = num;

        /*
         * Check mapper is ready.
         */
        // fd_map = open(mapper.c_str(),O_WRONLY);
        // if (fd_map < 0)
        // {
        //     ss.str("");
        //     ss << __func__ << "/open mapper fail> mapper=" << mapper;
        //     ss << ",rtn=" << fd_map;
        //     ss << ",errno=" << errno;
        //     throw std::runtime_error(ss.str());
        // }
        
        /*
         * Check unmapper is ready.
         */
        // fd_unmap = open(unmapper.c_str(),O_WRONLY);
        // if (fd_unmap < 0)
        // {
        //     ss.str("");
        //     ss << __func__ << "/open unmapper fail> unmapper=" << unmapper;
        //     ss << ",rtn=" << fd_unmap;
        //     ss << ",errno=" << errno;
        //     throw std::runtime_error(ss.str());
        // }

        /*
         * map operation directory.
         */
        // ss.str("");
        // ss << this->num;
        // // unmap first.
        // int write_rtn = write(fd_unmap,ss.str().c_str(),sizeof(ss.str().c_str()));
        // // map operation directory.
        // write_rtn = write(fd_map,ss.str().c_str(),sizeof(ss.str().c_str()));
        // if (write_rtn < 0)
        // {
        //     ss.str("");
        //     ss << __func__ << "/map operation node fail> ";
        //     ss << "rtn=" << write_rtn;
        //     ss << ",errno=" << errno;
        //     throw std::runtime_error(ss.str());
        // }
        // usleep(1000);
    }
    int pin::open_operation_node(const std::string& name,const int& flag)
    {
        int rtn = -1;
        std::stringstream ss;
        ss << __func__ << "> ";
        ss << "node=/sys/class/gpio/gpio" << this->num << "/" << name ;
#if (defined debug)
        std::cout << __func__ << "/open()> node=" << ss.str() << std::endl;
#endif
        rtn = open(ss.str().c_str(),flag);
        if (rtn < 0)
        {
            ss << ",rtn=" << rtn;
            ss << ",errno=" << errno;
            throw std::runtime_error(ss.str());
        }
        return rtn;
    }
    pin::~pin()
    {
        // if (fd_map >= 0) close(fd_map);
        // if (fd_unmap >= 0) close(fd_unmap);
    }
    pin::direction pin::get_direction()
    {
        int fd = open_operation_node("direction",O_RDONLY);
        const int len = 16;
        char buf[len] = {0};
        pin::direction rtn = pin::direction::undefined;
        int read_rtn = read(fd,buf,sizeof(buf));
        if (read_rtn < 0)
        {
            std::stringstream ss;
            ss << __func__ << "/read direction fail> rtn=" << read_rtn;
            ss << ",errno=" << errno;
            throw std::runtime_error(ss.str());
        }
        if (strcmp(buf,"out")==0)
        {
            rtn = pin::direction::output;
        }
        else if (strcmp(buf,"in")==0)
        {
            rtn = pin::direction::input;
        }
        close(fd);
        return rtn;
    }
    pin::state pin::get_state()
    {
        int fd = open_operation_node("value",O_RDONLY);
        const int len = 16;
        char buf[len] = {0};
        pin::state rtn = pin::state::undefined;
        int read_rtn = read(fd,buf,sizeof(buf));
        if (read_rtn < 0)
        {
            std::stringstream ss;
            ss << __func__ << "/read value fail> rtn=" << read_rtn;
            ss << ",errno=" << errno;
            throw std::runtime_error(ss.str());
        }
        if (buf[0]=='0')
        {
            rtn = pin::state::low;
        }
        else if (buf[0]=='1')
        {
            rtn = pin::state::high;
        }
        close(fd);
        return rtn;
    }
    void pin::set_direction(const pin::direction& value)
    {
        int fd = open_operation_node("direction",O_WRONLY);
        std::string data("in");
        if (value == pin::direction::output)
        {
            data = "out";
        }
        int write_rtn = write(fd,data.c_str(),sizeof(data.c_str()));
        if (write_rtn < 0)
        {
            std::stringstream ss;
            ss << __func__ << "/write value fail> rtn=" << write_rtn;
            ss << ",errno=" << errno;
            throw std::runtime_error(ss.str());
        }
        close(fd);
    };
    void pin::set_state(const pin::state& value)
    {
        int fd = open_operation_node("value",O_WRONLY);
        std::string data("0");
        if (value == pin::state::high)
        {
            data = "1";
        }
#if (defined debug)
        std::cout << __func__ << "/data=" << data << ",len=" << sizeof(data.c_str()) << std::endl;
#endif
        int write_rtn = write(fd,data.c_str(),sizeof(data.c_str()));
        if (write_rtn < 0)
        {
            std::stringstream ss;
            ss << __func__ << "/write value fail> rtn=" << write_rtn;
            ss << ",errno=" << errno;
            throw std::runtime_error(ss.str());
        }
        close(fd);
    };
    int pin::get_number()
    {
        return num;
    }
};

#if (defined debug)
    int main(int argc,char** argv)
    {
        gpio_syslib::pin led(227);
        gpio_syslib::pin input(228);
        led.set_direction(gpio_syslib::pin::direction::output);
        input.set_direction(gpio_syslib::pin::direction::input);

        gpio_syslib::pin::state st = gpio_syslib::pin::state::low;
        for ( int i = 0 ; i < 20 ; ++i )
        {
            if (st == gpio_syslib::pin::state::low)
            {
                st = gpio_syslib::pin::state::high;
            }
            else
            {
                st = gpio_syslib::pin::state::low;
            }
            led.set_state(st);

            if (input.get_state() == gpio_syslib::pin::state::low)
            {
                printf("input(%d) is low.\n",input.get_number());
            }
            else if (input.get_state() == gpio_syslib::pin::state::high)
            {
                printf("input(%d) is high.\n",input.get_number());
            }
            sleep(1);
        }
    }
#endif
